Deploy django applications with nginx, uwsgi, virtualenv, south, git and fabric, part 5
You can also be interested in:
This is the fifth and last part of the django deploy environment, you may find the fourth part here.
In this part we'll see how to automate the deployment using fabric.
What the hell have fabric to do?
Well, fabric have to do all the work for me in the sense that once I've done some changes to my project and pushed them to the bare repository I want to update the production environment with only one command. I'd like also to setup initially the whole environment with only one command.
How does it work?
First of all install it, so with the virtualenv activated:
$ pip install fabric
Done.
Now reading from the official site:
Fabric is a Python (2.5 or higher) library and command-line tool for streamlining the use of SSH for application deployment or systems administration tasks. It provides a basic suite of operations for executing local or remote shell commands (normally or via sudo) and uploading/downloading files, as well as auxiliary functionality such as prompting the running user for input, or aborting execution.
Fabric provides a command line tool (fab) which executes the tasks indicated in a fabfile.py. Such file contains all the operation to do in remote (and also in locale if desired) in order to update the environment.
Setup the environment with fabric
When doing the initial setup we have to do the following operations (assuming that the remote virtualhost is ready as the remote user and the database, see previous posts):
- create all the directories we need
- clone the bare erpository in remote
- checkout the latest version
- install all the requirement (do you remember pip freeze?)
Deploy to remote with fabric
In this case we have to perform the following operations:
- checkout the latest revision
- install the requierments (if new ones are necessary)
-
doing some symlinks (we keep trace of a previous release if we want to do a rollback)
- migrate the database (South)
- restart the webserver and uwsgi
The fabfile.py
All these things are achieved with the following fabfile (customize paths users etc...):
-
from fabric.api import *
-
# Default release is 'current'
-
env.release = 'current'
-
-
def production():
-
"""Production server settings"""
-
env.settings = 'production'
-
env.user = 'myproject'
-
env.path = '/home/%(user)s/sites/myproject' % env
-
env.hosts = ['mydomain.com']
-
-
def setup():
-
"""
-
Setup a fresh virtualenv and install everything we need so it's ready to deploy to
-
"""
-
run('mkdir -p %(path)s; cd %(path)s; virtualenv --no-site-packages .; mkdir releases; mkdir shared;' % env)
-
clone_repo()
-
checkout_latest()
-
install_requirements()
-
-
def deploy():
-
"""Deploy the latest version of the site to the server and restart nginx"""
-
checkout_latest()
-
install_requirements()
-
symlink_current_release()
-
migrate()
-
restart_server()
-
-
def clone_repo():
-
"""Do initial clone of the git repo"""
-
run('cd %(path)s; git clone /home/%(user)s/git/repositories/myproject.git repository' % env)
-
-
def checkout_latest():
-
"""Pull the latest code into the git repo and copy to a timestamped release directory"""
-
import time
-
env.release = time.strftime('%Y%m%d%H%M%S')
-
run("cd %(path)s/repository; git pull origin master" % env)
-
run('cp -R %(path)s/repository %(path)s/releases/%(release)s; rm -rf %(path)s/releases/%(release)s/.git*' % env)
-
-
def install_requirements():
-
"""Install the required packages using pip"""
-
run('cd %(path)s; %(path)s/bin/pip install -r ./releases/%(release)s/requirements.txt' % env)
-
-
def symlink_current_release():
-
"""Symlink our current release, uploads and settings file"""
-
with settings(warn_only=True):
-
run('cd %(path)s; rm releases/previous; mv releases/current releases/previous;' % env)
-
run('cd %(path)s; ln -s %(release)s releases/current' % env)
-
""" production settings"""
-
run('cd %(path)s/releases/current/; cp settings_%(settings)s.py myproject/settings.py' % env)
-
with settings(warn_only=True):
-
run('rm %(path)s/shared/static' % env)
-
run('cd %(path)s/releases/current/static/; ln -s %(path)s/releases/%(release)s/static %(path)s/shared/static ' %env)
-
-
def migrate():
-
"""Run our migrations"""
-
run('cd %(path)s/releases/current; ../../bin/python manage.py syncdb --noinput --migrate' % env)
-
-
def rollback():
-
"""
-
Limited rollback capability. Simple loads the previously current
-
version of the code. Rolling back again will swap between the two.
-
"""
-
run('cd %(path)s; mv releases/current releases/_previous;' % env)
-
run('cd %(path)s; mv releases/previous releases/current;' % env)
-
run('cd %(path)s; mv releases/_previous releases/previous;' %env)
-
restart_server()
-
-
def restart_server():
-
"""Restart the web server"""
-
with settings(warn_only=True):
-
sudo('kill -9 `cat /tmp/project-master_helpmamme.pid`')
-
sudo('rm /tmp/project-master_helpmamme.pid /tmp/uwsgi_helpmamme.sock')
-
run('cd %(path)s/releases/current; %(path)s/bin/uwsgi --ini %(path)s/releases/current/uwsgi.ini' % env)
-
sudo('/etc/init.d/nginx restart')
Now the good part.
Setup the production environment
Just cd into the locale project container folder, and
$ fab production setup
Just insert the required password when needed and the environment is set up.
What does it happens?
- the function production is called and some variables are defined, in particular the host, the path to the project container folder and the user with ssh access.
-
the function setup is called, so:
- the previous path is created
- a new virtualenv is created inside path
- two directories: releases and shared are created inside path
- the bare repository is cloned inside path/repository
- the new repository is updated (pull) looking at the bare one, and then copied into a subfolder of path/releases which name contains the actual datetime (the .git directory is removed).
-
all the project requirements are installed reading the requirements.txt.
So now we have the bare repository connected to the remote project repository and the locale repository that allow to keep the project syncronized with the local version. We have a releases folder which contains the last release of the project. We have our new virtualen with all the project dependencies installed.
Submit changes to the production environment
Just type
fab production deploy
Done!
What does it happens?
- the function production, same as above.
-
the function deploy is called, so:
- the new repository is updated (pull) looking at the bare one, and then copied into a subfolder of path/releases which name contains the actual datetime (the .git directory is removed).
- all the project requirements are installed reading the requirements.txt (if something has changed since the setup).
- a symbolic link is created in order to serve the last project version at the path releases/current. At the same time the current release (if any) is moved to releases/previous, and the previous (if any) is deleted.
- the settings.py is overwritten by the production_settings.py
- a symbolic link is created in order to serve the current release static folder at the path shared (the old shared symlink is deleted first)
- the database is migrated with no input (only the database structure is touched)
-
the web server is restarted and so the uwsgi application
So if all goes well the new release is up and running.
What if something went wrong?
Don't worry, just perform a rollback:
$ fab production rollback
and the previous version is restored
What's next
Luckily this series of posts ends here. Surely I'll write a summary post which will contain all the main arguments written in these five episodes with links to the specific parts. For questions problems etc... feel free to comment here.
What if we want to set up the project on another machine?
We may work from home and office for example, if you're interested read here.
Bibliography
Such series of posts came from a wild google search, there are some good articles about these stuffs and all gets out in the first google results, I've learned much from any of them, above all for the generation of the fabfile and the creation of the entire architecture. Then I've tried to collect all these things together and write something more recent than the used sources.
See also
Your Smartwatch Loves Tasker!
Your Smartwatch Loves Tasker!
Featured
Archive
- 2021
- 2020
- 2019
- 2018
- 2017
- Nov
- Oct
- Aug
- Jun
- Mar
- Feb
- 2016
- Oct
- Jun
- May
- Apr
- Mar
- Feb
- Jan
- 2015
- Nov
- Oct
- Aug
- Apr
- Mar
- Feb
- Jan
- 2014
- Sep
- Jul
- May
- Apr
- Mar
- Feb
- Jan
- 2013
- Nov
- Oct
- Sep
- Aug
- Jul
- Jun
- May
- Apr
- Mar
- Feb
- Jan
- 2012
- Dec
- Nov
- Oct
- Aug
- Jul
- Jun
- May
- Apr
- Jan
- 2011
- Dec
- Nov
- Oct
- Sep
- Aug
- Jul
- Jun
- May